﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ThreeByte.TaskModel;
using System.ComponentModel;
using ThreeByte.ActiveDeck.Config;
using ThreeByte.ActiveDeck.Service;
using System.Windows.Controls;
using ThreeByte.ActiveDeck.Content;
using System.Windows.Threading;
using System.Xml.Linq;
using ThreeByte.ActiveDeck.Data;
using System.Diagnostics;
using System.IO;
using log4net;

namespace ThreeByte.ActiveDeck.AddIn.Config
{
    public class UploadResourceTask : AsyncTaskBase
    {
        private static readonly ILog log = LogManager.GetLogger(typeof(UploadResourceTask));

        private string PresentationID;
        private GraphicResource Resource;
        private VideoResource VideoResource;
        private WebServiceClient Client;
        private static readonly int BLOCK_SIZE = 32 * 1024;  //32kb

        public UploadResourceTask(string presentation, GraphicResource resource, WebServiceClient client) {
            PresentationID = presentation;
            Resource = resource;
            Client = client;
            Name = string.Format("Uploading: {0}", Resource.Name);
        }

        public UploadResourceTask(string presentation, VideoResource resource, WebServiceClient client) {
            PresentationID = presentation;
            VideoResource = resource;
            Client = client;
            Name = string.Format("Uploading: {0}", VideoResource.Name);
        }

        public override bool Setup() {
            return base.Setup();
        }


        protected override void Run(BackgroundWorker bw, DoWorkEventArgs e) {

            if (bw.CancellationPending) {
                e.Cancel = true;
                return;
            }

            

            //Decide if the block needs to be segmented
            if ((Resource != null) && (Resource.GraphicBytes.Length < BLOCK_SIZE)) {
                //Easy case
                Status = string.Format("Uploading...");
                bool result = Client.UploadResource(PresentationID, Resource.ID.ToString(), Resource);
                if (!result) {
                    HasError = true;
                    Error = "Resource could not be uploaded";
                    return;
                }
            } else {
                if(bw.CancellationPending) {
                    e.Cancel = true;
                    return;
                }
                Status = "Segmenting...";
                //Break up the block and send the blocks one at a time
                ByteBlockGenerator newGenerator;
                string resourceId;
                FileStream fs = null;
                
                if(Resource != null) {
                    newGenerator = new ByteBlockGenerator(Resource.GraphicBytes, BLOCK_SIZE);
                    resourceId = Resource.ID.ToString();
                } else {
                    fs = VideoResource.GetStream();
                    newGenerator = new ByteBlockGenerator(fs, VideoResource.Length, BLOCK_SIZE);
                    resourceId = VideoResource.ID.ToString();
                }
                
                //Get current Resource Info to see if this exact file has already been uploaded
                try {
                    XElement currentResourceInfo = Client.GetResourceInfo(PresentationID, resourceId);

                    if(Resource != null) {
                        if(Resource.Matches(currentResourceInfo)) {
                            bw.ReportProgress(100);
                            Status = "Resource cached";
                            return;//Exit as complete
                        }
                    } else {
                        if(VideoResource.Matches(currentResourceInfo)) {
                            bw.ReportProgress(100);
                            Status = "Resource cached";
                            return;//Exit as complete
                        }
                    }

                } catch(Exception ex) {
                    //This resource does not exist / or is modified
                    //Continue as normal
                    log.Info("Resource not located", ex);
                }

                Stopwatch sw = Stopwatch.StartNew();
                
                int currentBlock = 0;
                foreach(ByteBlock bb in newGenerator){
                //while(newGenerator.HasMoreBlocks()) {
                    if(bw.CancellationPending) {
                        e.Cancel = true;
                        return;
                    }
                    string speed = string.Format("{0} Kb/s", (int)(currentBlock * BLOCK_SIZE / (sw.ElapsedMilliseconds + 1))); //Prevent Divide by zero
                    Status = string.Format("Uploading Block {0:000} @ {1}", currentBlock, speed);
                    bw.ReportProgress((int)(currentBlock++ * 100.0 / (double)(newGenerator.BlockCount + 1)));

                    //bool result = Client.UploadResourceBlock(PresentationID, Resource.ID.ToString(), newGenerator.GetNext());
                   
                    bool result = Client.UploadResourceBlock(PresentationID, resourceId, bb);
                    if(!result) {
                        Error = string.Format("Block could not be uploaded {0}", resourceId);
                        HasError = true;
                        return;
                    }
                }
                if(fs != null) {  //Close the Video resource handle
                    fs.Dispose();
                    fs = null;
                }

                if(bw.CancellationPending) {
                    e.Cancel = true;
                    return;
                }
                Status = string.Format("Committing Blocks");
                bw.ReportProgress((int)(currentBlock * 100.0 / (double)(newGenerator.BlockCount + 1)));
                XElement mediaDescription;
                if(Resource != null) {
                    mediaDescription = Resource.GetXmlHeader();
                } else {
                    mediaDescription = VideoResource.GetXmlHeader();
                }
                bool commitResult = Client.CommitResource(PresentationID, resourceId, newGenerator.BlockList, mediaDescription);
                if(!commitResult) {
                    Error = string.Format("Could not commit resource blocks: {0}", resourceId);
                    HasError = true;
                    return;
                }

            }

            bw.ReportProgress(100);
            Status = "Upload complete";   
        }

    }
}
